package besta.moon.command;

import java.sql.SQLException;

import besta.moon.engine.index.IndexDescription;
import besta.moon.engine.store.TableStorePage;
import besta.moon.engine.table.Column;
import besta.moon.engine.table.Columns;
import besta.moon.engine.table.ForeignKey;
import besta.moon.MoonException;
import besta.moon.MoonConnection;
import besta.moon.statement.MoonStatement;
import besta.moon.MoonCode;
import besta.moon.MoonLogger;
import besta.moon.MoonCommand;
import besta.moon.engine.Database;
import besta.moon.engine.Table;
import besta.moon.engine.index.IndexDescriptions;
import besta.moon.engine.table.ForeignKeys;
import besta.moon.sql.parser.SQLTokenizer;

public final class CommandTable extends MoonCommand {

    final private Columns columns = new Columns();
    final private IndexDescriptions indexes = new IndexDescriptions();
    final private ForeignKeys foreignKeys = new ForeignKeys();
    final private int tableCommandType;

    public CommandTable(MoonLogger log, String catalog, String name, int tableCommandType) {
        super(log);
        this.type = SQLTokenizer.TABLE;
        this.catalog = catalog;
        this.name = name;
        this.tableCommandType = tableCommandType;
    }

    public void addColumn(Column column) throws SQLException {
        addColumn(columns, column);
    }

    public void addIndex(IndexDescription indexDescription) throws SQLException {
        indexes.add(indexDescription);
    }

    public void addForeingnKey(ForeignKey key) {
        foreignKeys.add(key);
    }

    @Override
    public void executeImpl(MoonConnection con, MoonStatement st) throws Exception {
        Database database = catalog == null
                ? con.getDatabase(false)
                : Database.getDatabase(catalog, con, false);
        switch (tableCommandType) {
            case SQLTokenizer.CREATE -> database.createTable(con, name, columns, indexes, foreignKeys);
            case SQLTokenizer.ADD -> {
                con = new MoonConnection(con);
                Table oldTable = (Table) database.getTableView(con, name);
                TableStorePage tableLock = oldTable.requestLock(con, SQLTokenizer.ALTER, -1);
                String newName = "#" + System.currentTimeMillis() + this.hashCode();
                try {
                    Columns oldColumns = oldTable.columns;
                    Columns newColumns = oldColumns.copy();
                    for (int i = 0; i < columns.size(); i++) {
                        addColumn(newColumns, columns.get(i));
                    }

                    Table newTable = database.createTable(con, newName, newColumns, oldTable.indexes, indexes, foreignKeys);
                    StringBuilder buffer = new StringBuilder(256);
                    buffer.append("INSERT INTO ").append(newName).append('(');
                    for (int c = 0; c < oldColumns.size(); c++) {
                        if (c != 0) {
                            buffer.append(',');
                        }
                        buffer.append(oldColumns.get(c).getName());
                    }
                    buffer.append(")  SELECT * FROM ").append(name);
                    con.createStatement().execute(buffer.toString());

                    database.replaceTable(oldTable, newTable);
                } catch (Exception ex) {
                    try {
                        database.dropTable(con, newName);
                    } catch (Exception ex1) {
                    }
                    try {
                        indexes.drop(database);
                    } catch (Exception ex1) {
                    }
                    throw ex;
                } finally {
                    tableLock.freeLock();
                }
            }
            default -> throw new Error();
        }
    }

    private void addColumn(Columns cols, Column column) throws SQLException {
        if (cols.get(column.getName()) != null) {
            throw MoonException.create(MoonCode.COL_DUPLICATE, column.getName());
        }
        cols.add(column);
    }
}
